package cn.kai.util;

import cn.kai.base.TreeNode;
import lombok.experimental.UtilityClass;

import java.util.*;
import java.util.stream.Collectors;

@UtilityClass
public class TreeUtil {


    public <T extends TreeNode> List<T> buildTree(List<T> treeNodes) {
        //获取所有的根节点
        List<T> roots = treeNodes.stream().map(obj -> topNode(obj, treeNodes))
                //根据 id 去重
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(T::getId))),
                        ArrayList::new
                ))
                .stream()
                .sorted(Comparator.comparing(T::getSort))
                .collect(Collectors.toList());
        //转换为树
        return roots.stream().map(obj->subNode(obj,treeNodes)).collect(Collectors.toList());
    }

    /**
     * 向上找最高
     * @param t
     * @param treeNodes
     * @return
     */
    private <T extends TreeNode> T topNode(T t, List<T> treeNodes) {
        if (Objects.isNull(t.getParentId()) || 0 == t.getParentId()) {
            //root node
            return t;
        }
        if (treeNodes.stream().anyMatch(a -> Objects.equals(a.getId(), t.getParentId()))) {
            return topNode(treeNodes.stream().filter(a -> Objects.equals(a.getId(), t.getParentId())).findFirst().get(), treeNodes);
        } else {
            return t;
        }
    }

    /**
     * 向下找最低
     *
     * @param obj
     * @param list
     * @return
     */
    private <T extends TreeNode> T subNode(T obj, List<T> list) {
        T t =(T) ConvertUtils.beanProcess(obj, obj.getClass());
        if (Objects.isNull(t.getSort())){
            t.setSort(1);
        }
        if (list.stream().noneMatch(sub->obj.getId().equals(sub.getParentId()))){
            //确认为叶子节点
            t.addChildrenList(Collections.emptyList());
        }else {
            //还有子节点
            t.addChildrenList(subListNode(obj,list));
        }
        return t;
    }

    private <T extends TreeNode> List<T> subListNode(T obj, List<T> list) {
        try {
            //获取所有的子节点
            List<T> subNodeList = list.stream().filter(sub -> sub.getParentId().equals(obj.getId())).collect(Collectors.toList());
            return subNodeList.stream().map(m->subNode(m,list))
                    .sorted(Comparator.comparing(T::getSort))
                    .collect(Collectors.toList());
        }catch (Exception e){
            return null;
        }

    }

    /**
     * 两层循环实现建树
     *
     * @param treeNodes 传入的树节点列表
     * @return
     */
    public <T extends TreeNode> List<T> build(List<T> treeNodes, Long root) {
        List<T> trees = new ArrayList<>();
        for (T treeNode : treeNodes) {
            if (root.equals(treeNode.getParentId())) {
                trees.add(treeNode);
                trees.sort(Comparator.comparing(TreeNode::getSort));
            }
            for (T it : treeNodes) {
                if (it.getParentId().equals(treeNode.getId())) {
                    treeNode.addChildren(it);
                    treeNode.getChildren().sort(Comparator.comparing(T::getSort));
                }
            }
        }
        return trees;
    }

    /**
     * 使用递归方法建树
     *
     * @param treeNodes
     * @return
     */
    public <T extends TreeNode> List<T> buildByRecursive(List<T> treeNodes, Long root) {
        List<T> trees = new ArrayList<T>();
        for (T treeNode : treeNodes) {
            if (root.equals(treeNode.getParentId())) {
                trees.add(findChildren(treeNode, treeNodes));
            }
        }
        return trees;
    }

    /**
     * 递归查找子节点
     *
     * @param treeNodes
     * @return
     */
    public <T extends TreeNode> T findChildren(T treeNode, List<T> treeNodes) {
        for (T it : treeNodes) {
            if (treeNode.getId().equals(it.getParentId())) {
                if (treeNode.getChildren() == null) {
                    treeNode.setChildren(new ArrayList<>());
                }
                treeNode.addChildren(findChildren(it, treeNodes));
            }
        }
        return treeNode;
    }

}
